﻿using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Web;

namespace IEMSOFT.Foundation.Log
{
    public enum LogRequestVariableType
    {
        RequestId,
        RequestUrl,
        ClientDeviceId,
        ClientIP,
        Request,
        Response,
        RequestBeginTime,
        RequestEndTime,
        UserAgent,
        UserId
    }

    public interface ILogRequestVariable
    {
        Func<IDictionary> GetContextItems { get; set; }
        string NewRequestId();
        void SetVariable(LogRequestVariableType varType, object val);
        void BeginInitVariables(bool shouldAddRequestVarialbe);
        void EndInitVariables();
        string GetCurrentRequestId(bool generateOneIfNotExists = true);
        object this[LogRequestVariableType type] { get; set; }
    }

    public class LogRequestVariable : ILogRequestVariable
    {
        public const string KEY_CONTEXT_ITEM_REQUEST_ID = "LoggerRequestVariable_RequestId";
        public const string KEY_CONTEXT_ITEM_REQUEST_URL = "LoggerRequestVariable_RequestUrl";
        public const string KEY_CONTEXT_ITEM_DEVICE_ID = "LoggerRequestVariable_ClientDeviceId";
        public const string KEY_CONTEXT_ITEM_CLIENT_IP = "LoggerRequestVariable_ClientIP";
        public const string KEY_CONTEXT_ITEM_REQUEST = "LoggerRequestVariable_Request";
        public const string KEY_CONTEXT_ITEM_RESPONSE = "LoggerRequestVariable_Response";
        public const string KEY_CONTEXT_ITEM_REQUEST_BEGIN_TIME = "LoggerRequestVariable_RequestBeginTime";
        public const string KEY_CONTEXT_ITEM_REQUEST_END_TIME = "LoggerRequestVariable_RequestEndTime";
        public const string KEY_CONTEXT_ITEM_USER_AGENT = "LoggerRequestVariable_UserAgent";
        public const string KEY_CONTEXT_ITEM_USER_USERID = "LoggerRequestVariable_USERID";

        private Func<IDictionary> _getContextItems = null;
        public Func<IDictionary> GetContextItems
        {
            get
            {
                return _getContextItems;
            }
            set
            {
                _getContextItems = value;
            }
        }

        private HttpContext CurrentContext { get { return HttpContext.Current; } }
        private HttpRequest CurrentRequest { get { return CurrentContext == null ? null : CurrentContext.Request; } }
        private HttpResponse CurrentResponse { get { return CurrentContext == null ? null : CurrentContext.Response; } }
        private IDictionary ContextItems
        {
            get
            {
                IDictionary contextItems = null;
                if (GetContextItems != null)
                    contextItems = GetContextItems();
                if (contextItems != null) return contextItems;

                if (CurrentContext != null)
                {
                    return CurrentContext.Items;
                }
                else
                {
#if DEBUG
                    throw new Exception("HttpContext is null");
#else
                    return null;
#endif
                }
            }
        }

        public object this[LogRequestVariableType type]
        {
            get
            {
                if (ContextItems == null) return null;
                switch (type)
                {
                    case LogRequestVariableType.RequestId:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_REQUEST_ID) ? ContextItems[KEY_CONTEXT_ITEM_REQUEST_ID] : null;
                    case LogRequestVariableType.RequestUrl:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_REQUEST_URL) ? ContextItems[KEY_CONTEXT_ITEM_REQUEST_URL]: null;
                    case LogRequestVariableType.ClientDeviceId:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_DEVICE_ID) ? ContextItems[KEY_CONTEXT_ITEM_DEVICE_ID] : null;
                    case LogRequestVariableType.ClientIP:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_CLIENT_IP) ? ContextItems[KEY_CONTEXT_ITEM_CLIENT_IP]: null;
                    case LogRequestVariableType.Request:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_REQUEST) ? ContextItems[KEY_CONTEXT_ITEM_REQUEST]: null;
                    case LogRequestVariableType.Response:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_RESPONSE) ? ContextItems[KEY_CONTEXT_ITEM_RESPONSE]: null;
                    case LogRequestVariableType.RequestBeginTime:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_REQUEST_BEGIN_TIME) ? ContextItems[KEY_CONTEXT_ITEM_REQUEST_BEGIN_TIME] : null;
                    case LogRequestVariableType.RequestEndTime:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_REQUEST_END_TIME) ? ContextItems[KEY_CONTEXT_ITEM_REQUEST_END_TIME] : null;
                    case LogRequestVariableType.UserAgent:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_USER_AGENT) ? ContextItems[KEY_CONTEXT_ITEM_USER_AGENT] : null;
                    case LogRequestVariableType.UserId:
                        return ContextItems.Contains(KEY_CONTEXT_ITEM_USER_USERID) ? ContextItems[KEY_CONTEXT_ITEM_USER_USERID] : null;
                    default:
                        throw new NotImplementedException();
                }
            }
            set
            {
                if (ContextItems != null)
                    SetVariable(type, value);
            }
        }

        public void SetVariable(LogRequestVariableType varType, object val)
        {
            switch (varType)
            {
                case LogRequestVariableType.RequestId:
                    ContextItems[KEY_CONTEXT_ITEM_REQUEST_ID] = val;
                    break;
                case LogRequestVariableType.RequestUrl:
                    ContextItems[KEY_CONTEXT_ITEM_REQUEST_URL] = val;
                    break;
                case LogRequestVariableType.ClientDeviceId:
                    ContextItems[KEY_CONTEXT_ITEM_DEVICE_ID] = val;
                    break;
                case LogRequestVariableType.ClientIP:
                    ContextItems[KEY_CONTEXT_ITEM_CLIENT_IP] = val;
                    break;
                case LogRequestVariableType.Request:
                    ContextItems[KEY_CONTEXT_ITEM_REQUEST] = val;
                    break;
                case LogRequestVariableType.Response:
                    ContextItems[KEY_CONTEXT_ITEM_RESPONSE] = val;
                    break;
                case LogRequestVariableType.RequestBeginTime:
                    ContextItems[KEY_CONTEXT_ITEM_REQUEST_BEGIN_TIME] = val;
                    break;
                case LogRequestVariableType.RequestEndTime:
                    ContextItems[KEY_CONTEXT_ITEM_REQUEST_END_TIME] = val;
                    break;
                case LogRequestVariableType.UserAgent:
                    ContextItems[KEY_CONTEXT_ITEM_USER_AGENT] = val;
                    break;
                case LogRequestVariableType.UserId:
                    ContextItems[KEY_CONTEXT_ITEM_USER_USERID] = val;
                    break;
                default:
                    throw new NotImplementedException();
            }
        }


        public string NewRequestId()
        {
            // return Guid.NewGuid().ToString().Replace("-", "");
            return DateTime.Now.ToUniqueStr();
        }

        public void BeginInitVariables(bool shouldAddRequestVarialbe)
        {
            try
            {
                if (CurrentRequest != null)
                {
                    if (this[LogRequestVariableType.RequestId] == null)
                    {
                        var requestId = NewRequestId();
                        this[LogRequestVariableType.RequestId] = requestId;
                        Debug.WriteLine("Generated New RequestId in ContextItems: " + requestId);
                    }
                    this[LogRequestVariableType.ClientIP] = CurrentRequest.UserHostAddress;
                    this[LogRequestVariableType.RequestUrl] = CurrentRequest.Url.AbsoluteUri;
                    this[LogRequestVariableType.UserAgent] = CurrentRequest.UserAgent;
                    this[LogRequestVariableType.RequestBeginTime] = DateTime.Now;
                    if (shouldAddRequestVarialbe)
                    {
                        var request = new StringBuilder();
                        var requestBody = string.Empty;
                        request.AppendLine("RequestHeaders->");
                        foreach (var item in CurrentRequest.Headers.AllKeys)
                        {
                            request.AppendLine(string.Format("Key:{0},Value:{1}", item, CurrentRequest.Headers[item]));
                        }
                        
                        request.AppendLine(string.Format("RequestBody length:{0}->",CurrentRequest.ContentLength));
                        if (!CurrentRequest.ContentType.Contains("multipart/form-data"))//由于上传文件，图片可能比较大，因此不记录
                        {
                            var oldPosition = CurrentRequest.InputStream.Position;
                            CurrentRequest.InputStream.Position = 0;
                            byte[] bytes = new byte[CurrentRequest.InputStream.Length];
                            CurrentRequest.InputStream.ReadAsync(bytes, 0, bytes.Length).Wait();
                            if (bytes != null && bytes.Length > 0)
                            {
                                if (CurrentRequest.ContentEncoding == null)
                                {
                                    requestBody = System.Text.Encoding.UTF8.GetString(bytes);
                                }
                                else
                                {
                                    requestBody = CurrentRequest.ContentEncoding.GetString(bytes);
                                }
                            }
                            CurrentRequest.InputStream.Position = oldPosition;
                        }
                        else
                        {
                            requestBody = "multipart/form-data";
                        }
                        request.Append(requestBody);
                        this[LogRequestVariableType.Request] = request.ToString();
                    }
                }
            }
            catch (Exception)
            {
#if DEBUG
                throw;
#endif
            }
        }

        public void EndInitVariables()
        {
            this[LogRequestVariableType.RequestEndTime] = DateTime.Now;
        }

        public string GetCurrentRequestId(bool generateOneIfNotExists = true)
        {
            var requestId = this[LogRequestVariableType.RequestId];
            if (requestId == null && generateOneIfNotExists)
                requestId = NewRequestId();
            return requestId == null ? "" : requestId.ToString();
        }
    }

}
